home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkError.c < prev    next >
C/C++ Source or Header  |  1995-03-10  |  9KB  |  301 lines

  1. /* 
  2.  * tkError.c --
  3.  *
  4.  *    This file provides a high-performance mechanism for
  5.  *    selectively dealing with errors that occur in talking
  6.  *    to the X server.  This is useful, for example, when
  7.  *    communicating with a window that may not exist.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. static char sccsid[] = "@(#) tkError.c 1.18 95/03/10 11:30:32";
  17.  
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * The default X error handler gets saved here, so that it can
  23.  * be invoked if an error occurs that we can't handle.
  24.  */
  25.  
  26. static int    (*defaultHandler) _ANSI_ARGS_((Display *display,
  27.             XErrorEvent *eventPtr)) = NULL;
  28.  
  29.  
  30. /*
  31.  * Forward references to procedures declared later in this file:
  32.  */
  33.  
  34. static int    ErrorProc _ANSI_ARGS_((Display *display,
  35.             XErrorEvent *errEventPtr));
  36.  
  37. /*
  38.  *--------------------------------------------------------------
  39.  *
  40.  * Tk_CreateErrorHandler --
  41.  *
  42.  *    Arrange for all a given procedure to be invoked whenever
  43.  *    certain errors occur.
  44.  *
  45.  * Results:
  46.  *    The return value is a token identifying the handler;
  47.  *    it must be passed to Tk_DeleteErrorHandler to delete the
  48.  *    handler.
  49.  *
  50.  * Side effects:
  51.  *    If an X error occurs that matches the error, request,
  52.  *    and minor arguments, then errorProc will be invoked.
  53.  *    ErrorProc should have the following structure:
  54.  *
  55.  *    int
  56.  *    errorProc(clientData, errorEventPtr)
  57.  *        caddr_t clientData;
  58.  *        XErrorEvent *errorEventPtr;
  59.  *    {
  60.  *    }
  61.  *
  62.  *    The clientData argument will be the same as the clientData
  63.  *    argument to this procedure, and errorEvent will describe
  64.  *    the error.  If errorProc returns 0, it means that it
  65.  *    completely "handled" the error:  no further processing
  66.  *    should be done.  If errorProc returns 1, it means that it
  67.  *    didn't know how to deal with the error, so we should look
  68.  *    for other error handlers, or invoke the default error
  69.  *    handler if no other handler returns zero.  Handlers are
  70.  *    invoked in order of age:  youngest handler first.
  71.  *
  72.  *    Note:  errorProc will only be called for errors associated
  73.  *    with X requests made AFTER this call, but BEFORE the handler
  74.  *    is deleted by calling Tk_DeleteErrorHandler.
  75.  *
  76.  *--------------------------------------------------------------
  77.  */
  78.  
  79. Tk_ErrorHandler
  80. Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData)
  81.     Display *display;        /* Display for which to handle
  82.                  * errors. */
  83.     int error;            /* Consider only errors with this
  84.                  * error_code (-1 means consider
  85.                  * all errors). */
  86.     int request;        /* Consider only errors with this
  87.                  * major request code (-1 means
  88.                  * consider all major codes). */
  89.     int minorCode;        /* Consider only errors with this
  90.                  * minor request code (-1 means
  91.                  * consider all minor codes). */
  92.     Tk_ErrorProc *errorProc;    /* Procedure to invoke when a
  93.                  * matching error occurs.  NULL means
  94.                  * just ignore matching errors. */
  95.     ClientData clientData;    /* Arbitrary value to pass to
  96.                  * errorProc. */
  97. {
  98.     register TkErrorHandler *errorPtr;
  99.     register TkDisplay *dispPtr;
  100.  
  101.     /*
  102.      * Find the display.  If Tk doesn't know about this display then
  103.      * it's an error:  panic.
  104.      */
  105.  
  106.     dispPtr = TkGetDisplay(display);
  107.     if (dispPtr == NULL) {
  108.     panic("Unknown display passed to Tk_CreateErrorHandler");
  109.     }
  110.  
  111.     /*
  112.      * Make sure that X calls us whenever errors occur.
  113.      */
  114.  
  115.     if (defaultHandler == NULL) {
  116.     defaultHandler = XSetErrorHandler(ErrorProc);
  117.     }
  118.  
  119.     /*
  120.      * Create the handler record.
  121.      */
  122.  
  123.     errorPtr = (TkErrorHandler *) ckalloc(sizeof(TkErrorHandler));
  124.     errorPtr->dispPtr = dispPtr;
  125.     errorPtr->firstRequest = NextRequest(display);
  126.     errorPtr->lastRequest = (unsigned) -1;
  127.     errorPtr->error = error;
  128.     errorPtr->request = request;
  129.     errorPtr->minorCode = minorCode;
  130.     errorPtr->errorProc = errorProc;
  131.     errorPtr->clientData = clientData;
  132.     errorPtr->nextPtr = dispPtr->errorPtr;
  133.     dispPtr->errorPtr = errorPtr;
  134.  
  135.     return (Tk_ErrorHandler) errorPtr;
  136. }
  137.  
  138. /*
  139.  *--------------------------------------------------------------
  140.  *
  141.  * Tk_DeleteErrorHandler --
  142.  *
  143.  *    Do not use an error handler anymore.
  144.  *
  145.  * Results:
  146.  *    None.
  147.  *
  148.  * Side effects:
  149.  *    The handler denoted by the "handler" argument will not
  150.  *    be invoked for any X errors associated with requests
  151.  *    made after this call.  However, if errors arrive later
  152.  *    for requests made BEFORE this call, then the handler
  153.  *    will still be invoked.  Call XSync if you want to be
  154.  *    sure that all outstanding errors have been received
  155.  *    and processed.
  156.  *
  157.  *--------------------------------------------------------------
  158.  */
  159.  
  160. void
  161. Tk_DeleteErrorHandler(handler)
  162.     Tk_ErrorHandler handler;    /* Token for handler to delete;
  163.                  * was previous return value from
  164.                  * Tk_CreateErrorHandler. */
  165. {
  166.     register TkErrorHandler *errorPtr = (TkErrorHandler *) handler;
  167.     register TkDisplay *dispPtr = errorPtr->dispPtr;
  168.  
  169.     errorPtr->lastRequest = NextRequest(dispPtr->display) - 1;
  170.  
  171.     /*
  172.      * Every once-in-a-while, cleanup handlers that are no longer
  173.      * active.  We probably won't be able to free the handler that
  174.      * was just deleted (need to wait for any outstanding requests to
  175.      * be processed by server), but there may be previously-deleted
  176.      * handlers that are now ready for garbage collection.  To reduce
  177.      * the cost of the cleanup, let a few dead handlers pile up, then
  178.      * clean them all at once.  This adds a bit of overhead to errors
  179.      * that might occur while the dead handlers are hanging around,
  180.      * but reduces the overhead of scanning the list to clean up
  181.      * (particularly if there are many handlers that stay around
  182.      * forever).
  183.      */
  184.  
  185.     dispPtr->deleteCount += 1;
  186.     if (dispPtr->deleteCount >= 10) {
  187.     register TkErrorHandler *prevPtr;
  188.     TkErrorHandler *nextPtr;
  189.     int lastSerial;
  190.  
  191.     dispPtr->deleteCount = 0;
  192.     lastSerial = LastKnownRequestProcessed(dispPtr->display);
  193.     errorPtr = dispPtr->errorPtr;
  194.     for (errorPtr = dispPtr->errorPtr, prevPtr = NULL;
  195.         errorPtr != NULL;  errorPtr = nextPtr) {
  196.         nextPtr = errorPtr->nextPtr;
  197.         if ((errorPtr->lastRequest != -1)
  198.             && (errorPtr->lastRequest <= lastSerial)) {
  199.         if (prevPtr == NULL) {
  200.             dispPtr->errorPtr = nextPtr;
  201.         } else {
  202.             prevPtr->nextPtr = nextPtr;
  203.         }
  204.         ckfree((char *) errorPtr);
  205.         continue;
  206.         }
  207.         prevPtr = errorPtr;
  208.     }
  209.     }
  210. }
  211.  
  212. /*
  213.  *--------------------------------------------------------------
  214.  *
  215.  * ErrorProc --
  216.  *
  217.  *    This procedure is invoked by the X system when error
  218.  *    events arrive.
  219.  *
  220.  * Results:
  221.  *    If it returns, the return value is zero.  However,
  222.  *    it is possible that one of the error handlers may
  223.  *    just exit.
  224.  *
  225.  * Side effects:
  226.  *    This procedure does two things.  First, it uses the
  227.  *    serial #  in the error event to eliminate handlers whose
  228.  *    expiration serials are now in the past.  Second, it
  229.  *    invokes any handlers that want to deal with the error.
  230.  *
  231.  *--------------------------------------------------------------
  232.  */
  233.  
  234. static int
  235. ErrorProc(display, errEventPtr)
  236.     Display *display;            /* Display for which error
  237.                      * occurred. */
  238.     register XErrorEvent *errEventPtr;    /* Information about error. */
  239. {
  240.     register TkDisplay *dispPtr;
  241.     register TkErrorHandler *errorPtr;
  242.  
  243.     /*
  244.      * See if we know anything about the display.  If not, then
  245.      * invoke the default error handler.
  246.      */
  247.  
  248.     dispPtr = TkGetDisplay(display);
  249.     if (dispPtr == NULL) {
  250.     goto couldntHandle;
  251.     }
  252.  
  253.     /*
  254.      * Otherwise invoke any relevant handlers for the error, in order.
  255.      */
  256.  
  257.     for (errorPtr = dispPtr->errorPtr; errorPtr != NULL;
  258.         errorPtr = errorPtr->nextPtr) {
  259.     if ((errorPtr->firstRequest > errEventPtr->serial)
  260.         || ((errorPtr->error != -1)
  261.             && (errorPtr->error != errEventPtr->error_code))
  262.         || ((errorPtr->request != -1)
  263.             && (errorPtr->request != errEventPtr->request_code))
  264.         || ((errorPtr->minorCode != -1)
  265.             && (errorPtr->minorCode != errEventPtr->minor_code))
  266.         || ((errorPtr->lastRequest != -1)
  267.             && (errorPtr->lastRequest < errEventPtr->serial))) {
  268.         continue;
  269.     }
  270.     if (errorPtr->errorProc == NULL) {
  271.         return 0;
  272.     } else {
  273.         if ((*errorPtr->errorProc)(errorPtr->clientData,
  274.             errEventPtr) == 0) {
  275.         return 0;
  276.         }
  277.     }
  278.     }
  279.  
  280.     /*
  281.      * See if the error is a BadWindow error.  If so, and it refers
  282.      * to a window that still exists in our window table, then ignore
  283.      * the error.  Errors like this can occur if a window owned by us
  284.      * is deleted by someone externally, like a window manager.  We'll
  285.      * ignore the errors at least long enough to clean up internally and
  286.      * remove the entry from the window table.
  287.      */
  288.  
  289.     if ((errEventPtr->error_code == BadWindow) && (Tk_IdToWindow(display,
  290.         (Window) errEventPtr->resourceid) != NULL)) {
  291.     return 0;
  292.     }
  293.  
  294.     /*
  295.      * We couldn't handle the error.  Use the default handler.
  296.      */
  297.  
  298.     couldntHandle:
  299.     return (*defaultHandler)(display, errEventPtr);
  300. }
  301.